import secrets, time, random
import requests

class Struct_CveReport:
    def __init__(self):
        self.cve_number = ""
        self.productid = ""
        self.framework = ""
        self.influence = int()
        self.detail = ""
        self.solution = list()
        # solution[fix_pack1, fix_pack2,...]
        # fix_pack = {"packname": "",
        #             "status": "",
        #             "version": "",
        #             "sa_no": ""}

INFLUENCE_INFO = {"-1": "漏洞网页不存在",
                  "4": "不影响",
                  "2": "影响",
                  "3": "不计划修复",
                  "99": "没有该版本系统信息",
                  "-99": "网络请求失败超过5次"}

class KylinCveReport:
    CVE_URL = "https://server.kylinos.cn:18089/api/cve_manager/own_cve_info"  # CVE搜索接口

    # 输入信息：1、CVE编号 2、产品id 3、cpu架构
    def __init__(self, cve_number, productid, framework):
        self._cve_number = cve_number
        self._productid  = productid
        self._framework = framework
        self._response_detail = self._get_cve_response          # 提前获取post请求结果，避免每次都重复调用

    # 获取cve网页返回信息，POST请求
    @property
    def _get_cve_response(self) -> dict:
        UserAgent_List = [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.84 Safari/537.36 RuxitSynthetic/1.0 v100801619688 t564353316429258614 athccb4ad0b altpub cvcv=2 smf=0",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 GLS/100.10.9989.100",
            "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4494.0 Safari/537.36",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.18 Safari/525.19",
            "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.462.0 Safari/534.3"]
        headers = {
            "User-Agent": secrets.choice(UserAgent_List),
            "Host": "server.kylinos.cn:18089",
            "Connection": "keep-alive"}
        data = {"all_title": self._cve_number}
        post_error = 0
        while True:
            try:
                response = requests.post(url=KylinCveReport.CVE_URL, data=data, headers=headers, verify=False).json()
                break
            except:
                if post_error >= 5:
                    response = {"detail": {"error": ""}}
                    response["detail"]["error"] = "-99"
                    break
                post_error = post_error + 1
                print(f"{self._cve_number}：请求失败{post_error}次")
        # pprint.pprint(response["detail"])
        return response["detail"]

    # 漏洞影响状态
    @property
    def _influence(self) -> int:
        if not self._response_detail :
            # 返回值没有，说明漏洞未被收录，返回值 -1
            return -1
        elif "error" in self._response_detail.keys() and self._response_detail["error"] == "-99":
            return -99
        else:
            # 根据POST请求返回的数据中product_component_list里的status字段，来决定影响状态
            # 2 —— 影响  4 —— 不影响  99 —— 没有该版本系统的数据   -99 —— 网络请求失败超过5次
            # 其中 2， 4 是由漏洞平台返回值提供。99由代码自定义
            component_list = self._response_detail["product_component_list"]
            influence_status = 99
            for mumber in component_list:
                if str(mumber["product_id"]) == self._productid and mumber["framework"] == self._framework:
                    if int(mumber["status"]) < int(influence_status):
                        influence_status = mumber["status"]
                    # pprint.pprint(mumber)
            return int(influence_status)

    # 漏洞详情
    @property
    def _detail(self) -> str:
        cve_detail = self._response_detail["cve_detail"]
        return cve_detail


    # 解决方案
    @property
    def _solution(self) -> list:
        product_compnent_list = self._response_detail["product_component_list"]
        solution = []
        for mumber in product_compnent_list:
            if str(mumber["product_id"]) == self._productid and mumber["framework"] == self._framework:
                if mumber["status"] != "4":
                    fix_pack = {"packname": mumber["component_name"],
                                "status": mumber["status"],
                                "version": mumber["version"],
                                "sa_no": mumber["sa_no"]}
                    solution.append(fix_pack)
        return solution

    @property
    def report(self) -> Struct_CveReport:
        # cve报告的结构体
        cve_report = Struct_CveReport()
        cve_report.influence = self._influence
        if cve_report.influence > 0:  # 判断该CVE网站是否存在有效数据
            # 存在有效数据。
            cve_report.detail = self._detail
            cve_report.solution = self._solution
        return cve_report




def get_all_cvereports(cvelist, productid, framework) -> list:
    all_cvereports = []
    count = 0
    for cve_number in cvelist:
        count = count + 1
        print("{}/{}  漏洞编号: {} ".format(count, len(cvelist), cve_number))
        cvereport = KylinCveReport(cve_number, productid, framework).report
        all_cvereports.append(cvereport)
        print("状态： {}，  解决方案：{}".format(INFLUENCE_INFO.get(str(cvereport.influence), "返回值异常"),["{}: {}".format(fix_pack["packname"], fix_pack["version"]) for fix_pack in cvereport.solution]))
        # 防反爬虫
        sleeptime = random.uniform(1,3)
        print(f"防反爬虫休眠{sleeptime}秒")
        time.sleep(sleeptime) # 单线程+延时，减小服务端压力
    return all_cvereports








